﻿// <copyright file="PointSeriesData.cs" company="Oleg V. Polikarpotchkin">
// Copyright © 2008-2009 Oleg V. Polikarpotchkin. All Right Reserved
// </copyright>
// <author>Oleg V. Polikarpotchkin</author>
// <email>ov-p@yandex.ru</email>
// <date>2009-08-19</date>
// <summary>OpenWPFChart library. PointSeriesData class contains set of 2D-3D data points.</summary>
// <revision>$Id$</revision>

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;

namespace OpenWPFChart.Parts
{
	/// <summary>
	/// 2D PointSeriesData class contains set of data points.
	/// </summary>
	/// <typeparam name="TX">The type of the abscissa.</typeparam>
	/// <typeparam name="TY">The type of the ordinate.</typeparam>
	public class PointSeriesData<TX, TY> : ItemData, IPoint2DSeries
	{
		#region Points
		// TODO Change type of ScatteredPointsData.Points property to ObservableCollection and supply appropriate TypeConverter?
		IEnumerable<DataPoint<TX, TY>> points;
		/// <summary>
		/// Gets or sets the Points property.
		/// </summary>
		/// <value/>
		public IEnumerable<DataPoint<TX, TY>> Points
		{
			get { return points; }
			set
			{
				if (points != value)
				{
					if (points != null)
					{
						INotifyCollectionChanged iNotifyCollectionChanged
							= points as INotifyCollectionChanged;
						if (iNotifyCollectionChanged != null)
							iNotifyCollectionChanged.CollectionChanged -= CollectionChanged;
						else
						{
							IBindingList iBindingList = points as IBindingList;
							if (iBindingList != null)
								iBindingList.ListChanged -= ListChanged;
						}
					}

					points = value;

					if (points != null)
					{
						INotifyCollectionChanged iNotifyCollectionChanged
							= points as INotifyCollectionChanged;
						if (iNotifyCollectionChanged != null)
							iNotifyCollectionChanged.CollectionChanged += CollectionChanged;
						else
						{
							IBindingList iBindingList = points as IBindingList;
							if (iBindingList != null)
								iBindingList.ListChanged += ListChanged;
						}
					}

					NotifyPropertyChanged("Points");
				}
			}
		}

		/// <summary>
		/// Point observable collection changed in some way.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
		private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
		{
			NotifyPropertyChanged("Points");
		}

		/// <summary>
		/// Point binding list changed in some way.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
		private void ListChanged(object sender, ListChangedEventArgs e)
		{
			NotifyPropertyChanged("Points");
		}
		#endregion Points

		/// <inheritdoc />
		public override Type GetDimensionType(int index)
		{
			if (index < 0 || index > 1)
				throw new ArgumentOutOfRangeException("index");
			if (index == 0)
				return typeof(TX);
			else
				return typeof(TY);
		}

		/// <inheritdoc />
		public override bool DimensionEqual(int index, ItemData item)
		{
			if (index < 0 || index > 1)
				throw new ArgumentOutOfRangeException("index");
			ScatteredPointsData<TX, TY> typed = item as ScatteredPointsData<TX, TY>;
			if (typed == null)
				return false;
			return (from pt in Points select pt[index]).SequenceEqual(from pt in typed.Points select pt[index]);
		}

		#region IPoint2DSeries Members
		/// <inheritdoc />
		IEnumerable<Point2D> IPoint2DSeries.Points
		{
			get { return from pt in this.Points select new Point2D(pt.X, pt.Y); }
		}
		#endregion IPoint2DSeries Members

		#region ICloneable Override
		/// <summary>
		/// Creates a new object that is a copy of the current instance.
		/// </summary>
		/// <returns>
		/// A new object that is a copy of this instance.
		/// </returns>
		/// <remarks>This method is designed to provide the deep cloning facility if desired.</remarks>
		protected override object CloneInternal()
		{
			PointSeriesData<TX, TY> clone = base.CloneInternal() as PointSeriesData<TX, TY>;
			// Points.
			if (points != null)
			{
				INotifyCollectionChanged iNotifyCollectionChanged = clone.points as INotifyCollectionChanged;
				if (iNotifyCollectionChanged != null)
					iNotifyCollectionChanged.CollectionChanged += clone.CollectionChanged;
				else
				{
					IBindingList iBindingList = clone.points as IBindingList;
					if (iBindingList != null)
						iBindingList.ListChanged += clone.ListChanged;
				}
			}
			return clone;
		}
		#endregion ICloneable Override
	}

	/// <summary>
	/// 3D ScatteredPointsData class contains set of scattered points.
	/// </summary>
	/// <typeparam name="TX">The type of the abscissa.</typeparam>
	/// <typeparam name="TY">The type of the ordinate.</typeparam>
	/// <typeparam name="TZ">The type of z-dimension.</typeparam>
	public class PointSeriesData<TX, TY, TZ> : ItemData, IPoint3DSeries
	{
		#region Points
		// TODO Change type of ScatteredPointsData.Points property to ObservableCollection and supply appropriate TypeConverter?
		IEnumerable<DataPoint<TX, TY, TZ>> points;
		/// <summary>
		/// Gets or sets the Points property.
		/// </summary>
		/// <value/>
		public IEnumerable<DataPoint<TX, TY, TZ>> Points
		{
			get { return points; }
			set
			{
				if (points != value)
				{
					if (points != null)
					{
						INotifyCollectionChanged iNotifyCollectionChanged
							= points as INotifyCollectionChanged;
						if (iNotifyCollectionChanged != null)
							iNotifyCollectionChanged.CollectionChanged -= CollectionChanged;
						else
						{
							IBindingList iBindingList = points as IBindingList;
							if (iBindingList != null)
								iBindingList.ListChanged -= ListChanged;
						}
					}

					points = value;

					if (points != null)
					{
						INotifyCollectionChanged iNotifyCollectionChanged
							= points as INotifyCollectionChanged;
						if (iNotifyCollectionChanged != null)
							iNotifyCollectionChanged.CollectionChanged += CollectionChanged;
						else
						{
							IBindingList iBindingList = points as IBindingList;
							if (iBindingList != null)
								iBindingList.ListChanged += ListChanged;
						}
					}

					NotifyPropertyChanged("Points");
				}
			}
		}

		/// <summary>
		/// Point observable collection changed in some way.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
		private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
		{
			NotifyPropertyChanged("Points");
		}

		/// <summary>
		/// Point binding list changed in some way.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
		private void ListChanged(object sender, ListChangedEventArgs e)
		{
			NotifyPropertyChanged("Points");
		}
		#endregion Points

		/// <inheritdoc />
		public override Type GetDimensionType(int index)
		{
			if (index < 0 || index > 2)
				throw new ArgumentOutOfRangeException("index");
			switch (index)
			{
				case 0:
					return typeof(TX);
				case 1:
					return typeof(TY);
				default:
					return typeof(TZ);
			}
		}

		/// <inheritdoc />
		public override bool DimensionEqual(int index, ItemData item)
		{
			if (index < 0 || index > 2)
				throw new ArgumentOutOfRangeException("index");
			ScatteredPointsData<TX, TY, TZ> other = item as ScatteredPointsData<TX, TY, TZ>;
			if (other == null)
				return false;
			return (from pt in Points select pt[index]).SequenceEqual(from pt in other.Points select pt[index]);
		}

		#region IPoint3DSeries Members
		/// <inheritdoc />
		IEnumerable<Point3D> IPoint3DSeries.Points
		{
			get { return from pt in this.Points select new Point3D(pt.X, pt.Y, pt.Z); }
		}
		#endregion IPoint3DSeries Members

		#region ICloneable Override
		/// <summary>
		/// Creates a new object that is a copy of the current instance.
		/// </summary>
		/// <returns>
		/// A new object that is a copy of this instance.
		/// </returns>
		/// <remarks>This method is designed to provide the deep cloning facility if desired.</remarks>
		protected override object CloneInternal()
		{
			PointSeriesData<TX, TY, TZ> clone = base.CloneInternal() as PointSeriesData<TX, TY, TZ>;
			// Points.
			if (points != null)
			{
				INotifyCollectionChanged iNotifyCollectionChanged = clone.points as INotifyCollectionChanged;
				if (iNotifyCollectionChanged != null)
					iNotifyCollectionChanged.CollectionChanged += clone.CollectionChanged;
				else
				{
					IBindingList iBindingList = clone.points as IBindingList;
					if (iBindingList != null)
						iBindingList.ListChanged += clone.ListChanged;
				}
			}
			return clone;
		}
		#endregion ICloneable Override
	}
}
